// JSON-Funktionen. JS-Objekte werden dadurch nicht erweitert,
// da dies Probleme mit for..in-Schleifen ausloest (siehe: http://yuiblog.com/blog/2006/09/26/for-in-intrigue/)
// 2007-10-26, mn
// 2008-03-11, mn: Update: Kann jetzt auch Arrays als Values in Objects


// Allgemeine Funktion, die die Arbeit aufteilt
function toJSONString(value, w)
{
	switch (typeof value)
	{
		case 'string':
			return StringToJSONString(value);
		case 'number':
			return NumberToJSONString(value);
		case 'boolean':
			return BooleanToJSONString(value);
		case 'object':
			if (value instanceof Array)
			{
				return ArrayToJSONString(value, w);
			}
			else
			{
				return ObjectToJSONString(value, w);
			}
		default:
			return null;
	}
}

function ArrayToJSONString(array, w)
{
        var a = [],     // The array holding the partial texts.
            i,          // Loop counter.
            l = array.length,
            v;          // The value to be stringified.

		// For each value in this array...

        for (i = 0; i < l; i += 1) {
            v = array[i];
            switch (typeof v) {
            case 'object':

		// Serialize a JavaScript object value. Treat objects thats lack the
		// toJSONString method as null. Due to a specification error in ECMAScript,
		// typeof null is 'object', so watch out for that case.

                if (v) {
		    		if (v instanceof Array)
					{
						a.push(ArrayToJSONString(v, w));
					}
					else
					{
						a.push(ObjectToJSONString(v, w));
					}  
                } else {
                    a.push('null');
                }
                break;

            case 'string':
            	a.push(StringToJSONString(v));
            	break;
            case 'number':
            	a.push(NumberToJSONString(v));
            	break;
            case 'boolean':
                a.push(BooleantoJSONString(v));
                break;
            default:
                a.push('null');
            }
        }

		// Join all of the member texts together and wrap them in brackets.

        return '[' + a.join(',') + ']';
}


function BooleanToJSONString(value)
{
	return String(value);
}

function DateToJSONString(value)
{
	// Format integers to have at least two digits.
	function f(n)
	{
	    return n < 10 ? '0' + n : n;
    }
    return '"' + value.getUTCFullYear()   + '-' +
           f(value.getUTCMonth() + 1) + '-' +
           f(value.getUTCDate())      + 'T' +
           f(value.getUTCHours())     + ':' +
           f(value.getUTCMinutes())   + ':' +
           f(value.getUTCSeconds())   + 'Z"';
}


function NumberToJSONString(value)
{
	return isFinite(value) ? String(value) : 'null';
}


function ObjectToJSONString(obj, w)
{
        var a = [],     // The array holding the partial texts.
            k,          // The current key.
            i,          // The loop counter.
            v;          // The current value.

		// If a whitelist (array of keys) is provided, use it assemble the components
		// of the object.

        if (w) {
            for (i = 0; i < w.length; i += 1) {
                k = w[i];
                if (typeof k === 'string') {
                    v = this[k];
                    switch (typeof v) {
                    case 'object':

					// Serialize a JavaScript object value. Ignore objects that lack the
					// toJSONString method. Due to a specification error in ECMAScript,
					// typeof null is 'object', so watch out for that case.

                        if (v) {
				    		if (v instanceof Array)
							{
								a.push(StringToJSONString(k) + ':' +
				                ArrayToJSONString(v, w));
							}
							else
							{
								a.push(StringToJSONString(k) + ':' +
				                ObjectToJSONString(v, w));
							}                    	
                        } else {
                            a.push(StringToJSONString(k) + ':null');
                        }
                        break;

                    case 'string':
                    	a.push(StringToJSONString(k) + ':' + StringToJSONString(v));
                    	break;
                    case 'number':
                    	a.push(StringToJSONString(k) + ':' + NumberToJSONString(v));
                    	break;
                    case 'boolean':
                    	a.push(StringToJSONString(k) + ':' + BooleanToJSONString(v));
                    	break;

					// Values without a JSON representation are ignored.

                    }
                }
            }
        } else {
			// Iterate through all of the keys in the object, ignoring the proto chain
			// and keys that are not strings.

            for (k in obj) {
                if (typeof k === 'string' &&
                        Object.prototype.hasOwnProperty.apply(obj, [k])) {
                    v = obj[k];
                    switch (typeof v) {
                    case 'object':

					// Serialize a JavaScript object value. Ignore objects that lack the
					// toJSONString method. Due to a specification error in ECMAScript,
					// typeof null is 'object', so watch out for that case.
						
                        if (v) {
				    		if (v instanceof Array)
							{
								a.push(StringToJSONString(k) + ':' +
				                ArrayToJSONString(v, w));
							}
							else
							{
								a.push(StringToJSONString(k) + ':' +
				                ObjectToJSONString(v, w));
							}  
                        } else {
                            a.push(StringToJSONString(k) + ':null');
                        }
                        break;

                    case 'string':
                    	a.push(StringToJSONString(k) + ':' + StringToJSONString(v));
                    	break;
                    case 'number':
                    	a.push(StringToJSONString(k) + ':' + NumberToJSONString(v));
                    	break;
                    case 'boolean':
                        a.push(StringToJSONString(k) + ':' + BooleanToJSONString(v));
                        break;

					// Values without a JSON representation are ignored.

                    }
                }
            }
        }

		// Join all of the member texts together and wrap them in braces.
        return '{' + a.join(',') + '}';
    };
    
function StringToJSONString(value)
{

	// m is a table of character substitutions.
    var m = {
        '\b': '\\b',
        '\t': '\\t',
        '\n': '\\n',
        '\f': '\\f',
        '\r': '\\r',
        '"' : '\\"',
        '\\': '\\\\'
    };

	// If the string contains no control characters, no quote characters, and no
	// backslash characters, then we can simply slap some quotes around it.
	// Otherwise we must also replace the offending characters with safe
	// sequences.

    if (/["\\\x00-\x1f]/.test(value)) {
        return '"' + value.replace(/[\x00-\x1f\\"]/g, function (a) {
            var c = m[a];
            if (c) {
                return c;
            }
            c = a.charCodeAt();
            return '\\u00' + Math.floor(c / 16).toString(16) +
                                       (c % 16).toString(16);
        }) + '"';
    }
    return '"' + value + '"';
}


function parseJSON(s, filter)
{
    var j;

    function walk(k, v) {
        var i, n;
        if (v && typeof v === 'object') {
            for (i in v) {
                if (Object.prototype.hasOwnProperty.apply(v, [i])) {
                    n = walk(i, v[i]);
                    if (n !== undefined) {
                        v[i] = n;
                    }
                }
            }
        }
        return filter(k, v);
    }


	// Parsing happens in three stages. In the first stage, we run the text against
	// a regular expression which looks for non-JSON characters. We are especially
	// concerned with '()' and 'new' because they can cause invocation, and '='
	// because it can cause mutation. But just to be safe, we will reject all
	// unexpected characters.
	
	// We split the first stage into 4 regexp operations in order to work around
	// crippling deficiencies in IE's and Safari's regexp engines. First we replace
	// all backslash pairs with '@' (a non-JSON character). Second, we replace all
	// simple value tokens with ']' characters. Third, we delete all open brackets
	// that follow a colon or comma or that begin the text. Finally, we look to see
	// that the remaining characters are only whitespace or ']' or ',' or ':' or '{'
	// or '}'. If that is so, then the text is safe for eval.

    if (/^[\],:{}\s]*$/.test(s.replace(/\\./g, '@').
            replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(:?[eE][+\-]?\d+)?/g, ']').
            replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

			// In the second stage we use the eval function to compile the text into a
			// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
			// in JavaScript: it can begin a block or an object literal. We wrap the text
			// in parens to eliminate the ambiguity.

            j = eval('(' + s + ')');

			// In the optional third stage, we recursively walk the new structure, passing
			// each name/value pair to a filter function for possible transformation.

            return typeof filter === 'function' ? walk('', j) : j;
    }

	// If the text is not JSON parseable, then a SyntaxError is thrown.

    throw new SyntaxError('parseJSON');
}
